﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using MSNPSharp;
using CGLib;

namespace TienLen
{
    public partial class MSNClient : Form
    {
        bool msgclose = true;
        Timer timerPing = new Timer();

        private const string note1 = "Play Game!";
        private const string note2 = "Return back to current conversations";
        private const string note3 = "Return to current arranged game";

        private List<Guid> pendingPlaces = new List<Guid>();

        public MSNClient(PresenceStatus status)
        {
            InitializeComponent();
            timerPing.Tick += new EventHandler(timerPing_Tick);

            this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
            this.Tag = status;

            if (TienLen.Properties.Settings.Default.opt_msnptext)
            {
                cononline.DrawNode += new DrawTreeNodeEventHandler(cononline_DrawNode);
                cononline.DrawMode = TreeViewDrawMode.OwnerDrawAll;
            }
            else
            {
                cononline.Nodes[0].Text += string.Empty;
                cononline.Nodes[1].Text += string.Empty;

                Graphics g = Graphics.FromImage(new Bitmap(16, 16) as Image);
                g.Clear(Color.White);

                cononline.ImageList = new ImageList()
                {
                    ImageSize = new Size(16, 16),
                    Images = { new Bitmap(16, 16, g), Image.FromFile(Application.StartupPath + @"\img\other\wlm-online.png") },
                    ColorDepth = ColorDepth.Depth32Bit
                };
            }

            #region Add MSN Events

            SvrFunc.MSN.Nameserver.ContactOnline += new EventHandler<ContactStatusChangedEventArgs>(MSN_ContactOnline);
            SvrFunc.MSN.Nameserver.ContactOffline += new EventHandler<ContactStatusChangedEventArgs>(MSN_ContactOffline);

            SvrFunc.MSN.Nameserver.PingAnswer += new EventHandler<PingAnswerEventArgs>(MSN_PingAnswer);
            SvrFunc.MSN.Owner.PlacesChanged += new EventHandler<PlaceChangedEventArgs>(Owner_PlacesChanged);

            SvrFunc.MSN.Owner.StatusChanged += new EventHandler<StatusChangedEventArgs>(MSN_StatusChanged);
            SvrFunc.MSN.Owner.ScreenNameChanged += new EventHandler<EventArgs>(MSN_OwnerNameChanged);
            SvrFunc.MSN.Owner.PersonalMessageChanged += new EventHandler<EventArgs>(MSN_OwnerNameChanged);
            SvrFunc.MSN.MessageManager.TextMessageReceived += new EventHandler<TextMessageArrivedEventArgs>(MessageManager_TextMessageReceived);

            #endregion
        }

        #region MSN Events

        private void MSN_PingAnswer(object sender, PingAnswerEventArgs e)
        {
            if (InvokeRequired)
            {
                Invoke(new EventHandler<PingAnswerEventArgs>(MSN_PingAnswer), sender, e);
                return;
            }

            timerPing.Interval = e.SecondsToWait * 1000;
            timerPing.Start();
        }

        private void timerPing_Tick(object sender, EventArgs e)
        {
            SvrFunc.MSN.Nameserver.SendPing();
            timerPing.Stop();
        }

        private void MessageManager_TextMessageReceived(object sender, TextMessageArrivedEventArgs e)
        {
            if (InvokeRequired)
            {
                if (!this.Visible)
                    return;

                Invoke(new EventHandler<TextMessageArrivedEventArgs>(MessageManager_TextMessageReceived), sender, e);
                return;
            }

            bool success;
            MSNPacket packet = new MSNPacket((e as TextMessageArrivedEventArgs).TextMessage.Text, out success);

            if (success)
            {
                if (packet.Type == CGLib.Enums.MsgType.Invite)

                    switch (packet.Info[0])
                    {
                        case "A":

                            int indexFind = SvrFunc.GameGUID.FindIndex(delegate(Guid guidFind) { return guidFind == new Guid(packet.Info[1]); });

                            if (indexFind != -1)
                                new PopUp(AlertType.Invite, e, indexFind).Show(this);

                            break;

                        case "D":
                            if (packet.Info[1] != SvrFunc.MSN.Owner.Account) return;

                            scMain.Panel2.Controls.Add(
                                new MSNPlayerRoom(e.Sender, e.OriginalSender,
                                    SvrFunc.GameGUID.FindIndex(delegate(Guid guidFind) { return guidFind == new Guid(packet.Info[2]); })));

                            scMain.Panel2.Controls[1].BringToFront();

                            break;

                        case "F":
                            new PopUp(AlertType.Notify,
                                e.Sender.Name + " cannot place a slot for you as all slots have been taken.").Show(this);
                            break;
                    }
                else
                    msnMainMenu.AddTab(new MSNConvo(e.Sender, (e as TextMessageArrivedEventArgs).TextMessage.Text), e.Sender.Account, e.Sender.Name);
            }
        }

        private void Owner_PlacesChanged(object sender, PlaceChangedEventArgs e)
        {
            if (e.EndPointData.Id == NSMessageHandler.MachineGuid ||
                (pendingPlaces.Find(delegate(Guid guidFind) { return guidFind == e.EndPointData.Id; }) != Guid.Empty && e.Reason == PlaceChangedReason.SignedIn))
                return;

            else if (e.Reason == PlaceChangedReason.SignedOut)
            {
                pendingPlaces.Remove(e.EndPointData.Id);
                return;
            }

            if (InvokeRequired)
            {
                Invoke(new EventHandler<PlaceChangedEventArgs>(Owner_PlacesChanged), sender, e);
                return;
            }

            if (MessageBox.Show("Tien Len can only have your account logged onto this client only. \r\nThere are " + SvrFunc.MSN.Owner.PlaceCount.ToString() +
                " places logged onto this same account.\r\n\r\nDo you want to log off the other places logged onto this account?" +
                "\r\nThe MSN client will automatically log off if you choose \"No\".", "Multiple Places",
                MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
            {
                Dictionary<Guid, EndPointData> test = SvrFunc.MSN.Owner.EndPointData;

                foreach (KeyValuePair<Guid, EndPointData> keyvalue in SvrFunc.MSN.Owner.EndPointData)
                {
                    if (keyvalue.Key != Guid.Empty && keyvalue.Key != NSMessageHandler.MachineGuid)
                        SvrFunc.MSN.Owner.SignoutFrom(keyvalue.Key);
                }
            }
            else
            {
                msgclose = false;
                this.Close();
            }

            pendingPlaces.Add(e.EndPointData.Id);
        }

        private void MSN_OwnerNameChanged(object sender, EventArgs e)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new EventHandler<EventArgs>(MSN_OwnerNameChanged), sender, e);
                return;
            }

            owneropt.Text = SvrFunc.MSN.Owner.Name + " - " + SvrFunc.MSN.Owner.PersonalMessage.Message;
        }

        private void MSN_StatusChanged(object sender, StatusChangedEventArgs e)
        {
            Invoke(new MethodInvoker(ChangeStatus));
        }

        private void MSN_ContactOnline(object sender, ContactStatusChangedEventArgs e)
        {
            if (InvokeRequired)
            {
                Invoke(new EventHandler<ContactStatusChangedEventArgs>(MSN_ContactOnline), sender, e);
                return;
            }

            if (cononline.Nodes.Find(e.Contact.Account, true).Length != 0)
                return;

            else if (e.Contact == SvrFunc.MSN.Owner)
            {
                int revDot = Application.ProductVersion.LastIndexOf('.');

                SvrFunc.MSN.Owner.PersonalMessage = new PersonalMessage(SvrFunc.MSN.Owner.PersonalMessage.Message, MediaType.Games,
                    new string[] { "Tien Len Client", Application.ProductVersion.Substring(revDot + 1, Application.ProductVersion.Length - revDot - 1) });
            }

            if (e.Contact != SvrFunc.MSN.Owner)
                e.Contact.PersonalMessageChanged += new EventHandler<EventArgs>(Contact_PMChange);
            AddContact(e.Contact);
        }

        private void MSN_ContactOffline(object sender, ContactStatusChangedEventArgs e)
        {
            Invoke(new MethodInvoker(delegate() 
                {
                    e.Contact.PersonalMessageChanged -= Contact_PMChange;
                    cononline.Nodes[IsLoggedInClient(e.Contact) ? 0 : 1].Nodes.RemoveByKey(e.Contact.Account);
                }));
        }

        private void Contact_PMChange(object sender, EventArgs e)
        {
            if (InvokeRequired)
            {
                Invoke(new EventHandler<EventArgs>(Contact_PMChange), sender, e);
                return;
            }

            Contact objCon = (Contact)sender;

            if (IsLoggedInClient(objCon))
            {
                if (cononline.Nodes[1].Nodes.IndexOfKey(objCon.Account) != -1)
                    cononline.Nodes[1].Nodes.RemoveByKey(objCon.Account);

                objCon.PersonalMessageChanged -= Contact_PMChange;
                AddContact(objCon);
            }
        }

        #endregion

        public TreeNodeCollection OnlineUsers
        {
            get { return cononline.Nodes; }
        }

        private bool IsLoggedInClient(Contact contact)
        {
            return contact.ClientType != IMAddressInfoType.Yahoo && contact.PersonalMessage.MediaType == MediaType.Games && contact.PersonalMessage.CurrentMediaContent[0] == "Tien Len Client";
        }

        private void AddContact(Contact c)
        {
            MSNPlusName textCustom = new MSNPlusName(TienLen.Properties.Settings.Default.opt_msnptext ? c.Name : "na");

            TreeNode usenode = new TreeNode()
            {
                Tag = c,
                SelectedImageIndex = 1,
                ImageIndex = 1,
                Name = c.Account,
                Text = textCustom.Success ? textCustom.Text + " " : c.Name
            };

            int index = !IsLoggedInClient(c) ? 1 : 0;

            cononline.Nodes[index].Nodes.Add(usenode);
        }

        private void ChangeStatus()
        {
            ToolStripMenuItem[] items = new ToolStripMenuItem[] { onlineToolStripMenuItem, busyToolStripMenuItem, awayToolStripMenuItem, appearOfflineToolStripMenuItem };
            int check = 0;

            switch (SvrFunc.MSN.Owner.Status)
            {
                case PresenceStatus.Online:
                    {
                        owneropt.Image = Image.FromFile(Application.StartupPath + @"\img\other\wlm-online.png");
                        check = 0;
                        break;
                    }
                case PresenceStatus.Busy:
                    {
                        owneropt.Image = Image.FromFile(Application.StartupPath + @"\img\other\wlm-busy.png");
                        check = 1;
                        break;
                    }
                case PresenceStatus.Away:
                    {
                        owneropt.Image = Image.FromFile(Application.StartupPath + @"\img\other\wlm-away.png");
                        check = 2;
                        break;
                    }
                case PresenceStatus.Hidden:
                    {
                        owneropt.Image = Image.FromFile(Application.StartupPath + @"\img\other\wlm-offline.png");
                        check = 3;
                        break;
                    }
            }

            for (int x = 0; x < 4; x++)
                items[x].Checked = x == check ? true : false;
        }

        private void playGameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (playGameButton.Text == note1)
            {
                if (SvrFunc.MSN.Owner.Status != PresenceStatus.Hidden)
                {
                    GameSelection gameselRoom = new GameSelection();

                    if (gameselRoom.ShowDialog() == DialogResult.OK)
                    {
                        scMain.Panel2.Controls.Add(new MSNPlayerRoom(SvrFunc.MSN.Owner, SvrFunc.MSN.Owner, gameselRoom.listGames.SelectedIndex));
                        scMain.Panel2.Controls[1].BringToFront();

                        playGameButton.Text = note2;
                    }
                }
                else
                    MessageBox.Show("You cannot make a game while you are appearing offline.", "Unable to Make Game", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }

            else if (playGameButton.Text == note2)
            {
                scMain.Panel2.Controls[1].BringToFront();
                playGameButton.Text = note3;
            }

            else if (playGameButton.Text == note3)
            {
                scMain.Panel2.Controls[1].BringToFront();
                playGameButton.Text = note2;
            }
        }

        private void onlineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SvrFunc.MSN.Owner.Status = PresenceStatus.Online;
        }

        private void busyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SvrFunc.MSN.Owner.Status = PresenceStatus.Busy;
        }

        private void awayToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SvrFunc.MSN.Owner.Status = PresenceStatus.Away;
        }

        private void appearOfflineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SvrFunc.MSN.Owner.Status = PresenceStatus.Hidden;
        }

        private void changeNameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ChangeValue frmtemp = new ChangeValue(ChangeValueType.Name, SvrFunc.MSN.Owner.Name);

            if (frmtemp.ShowDialog(this) == DialogResult.OK)
                new System.Threading.Thread(delegate(object val) { SvrFunc.MSN.Owner.Name = val.ToString(); }).Start(frmtemp.Value);
        }

        private void changePMToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ChangeValue frmtemp = new ChangeValue(ChangeValueType.PersonalMsg, SvrFunc.MSN.Owner.PersonalMessage.Message);

            if (frmtemp.ShowDialog(this) == DialogResult.OK)
                new System.Threading.Thread(delegate(object val)
                    {
                        SvrFunc.MSN.Owner.PersonalMessage = new PersonalMessage(val.ToString(), SvrFunc.MSN.Owner.PersonalMessage.MediaType,
                            SvrFunc.MSN.Owner.PersonalMessage.CurrentMediaContent); 
                    }).Start(frmtemp.Value);
        }

        private void MSNClient_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (!msgclose || MessageBox.Show("Are you sure you want to log off MSN?", "Log off MSN?", MessageBoxButtons.YesNo,
                MessageBoxIcon.Information) == DialogResult.Yes)
            {
                SvrFunc.MSN.Nameserver.PingAnswer -= MSN_PingAnswer;
                SvrFunc.MSN.MessageManager.TextMessageReceived -= MessageManager_TextMessageReceived;

                SvrFunc.MSN.Nameserver.ContactOnline -= MSN_ContactOnline;
                SvrFunc.MSN.Nameserver.ContactOffline -= MSN_ContactOffline;

                SvrFunc.MSN.Disconnect();
                new MainMenu().Show();

                GC.Collect();
            }
            else
                e.Cancel = true;
        }

        private void cononline_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Button == MouseButtons.Left && scMain.Panel2.Controls.Count == 1 && e.Node.Tag != null)
            {
                Contact selContact = (Contact)e.Node.Tag;

                msnMainMenu.AddTab(new MSNConvo(selContact, string.Empty), selContact.Account, selContact.Name);
            }
        }

        private void MSNClient_Load(object sender, EventArgs e)
        {
            owneropt.Text = SvrFunc.MSN.Owner.Name;

            SvrFunc.MSN.Owner.Status = (PresenceStatus)this.Tag;
            this.Tag = null;
        }

        private void cononline_DrawNode(object sender, DrawTreeNodeEventArgs e)
        {
            // When you expand a node for the first time, the child nodes in that node will go straight to
            // the point where (X = Y = Height = Width = 0). That's why it's here :)
            if (e.Bounds.Width == 0 && e.Bounds.Height == 0)
                return;

            // Clear part of the bounds so that we don't get layered drawings
            e.Graphics.FillRectangle(new SolidBrush(SystemColors.Window), e.Bounds);

            MSNPlusName textCustom = new MSNPlusName(e.Node.Tag != null ? ((Contact)e.Node.Tag).Name : "NA");
            int recText = e.Bounds.X + (int)(cononline.Width * (e.Node.Tag != null ? 0.2f : 0.125f));
            bool isSelected = e.State == (TreeNodeStates.Selected | TreeNodeStates.Focused);

            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

            if (textCustom.Success)
            {
                int sizebuild = 0;

                int startBuildRect = 0;
                int sizeDrawRect = 0;

                // Draw the MSN Plus! custom background colours, if not selected
                if (!isSelected)
                {
                    for (int i = 0; i < textCustom.Values.Count; i++)
                    {
                        if (textCustom.Values[i].Background != SystemColors.Window)
                        {
                            if (textCustom.Values[i].EndBackground != SystemColors.Window)
                            {
                                // If the next index of custom text values has the same gradient colours, draw it up
                                // from there instead
                                if (textCustom.Values.Count > 1 && i != textCustom.Values.Count - 1 &&
                                    textCustom.Values[i].Background.Equals(textCustom.Values[i + 1].Background) &&
                                    textCustom.Values[i].EndBackground.Equals(textCustom.Values[i + 1].EndBackground))
                                {
                                    sizeDrawRect += (int)GetTextWidth(textCustom.Values[i], e.Graphics);
                                    continue;
                                }

                                e.Graphics.FillRectangle(new LinearGradientBrush(new Point(recText + startBuildRect, e.Bounds.Y),
                                    new Point(recText + startBuildRect + sizeDrawRect + (int)GetTextWidth(textCustom.Values[i], e.Graphics), e.Bounds.Y),
                                    textCustom.Values[i].Background, textCustom.Values[i].EndBackground), recText + startBuildRect, e.Bounds.Y,
                                    GetTextWidth(textCustom.Values[i], e.Graphics) + sizeDrawRect, e.Bounds.Height - 1);
                            }
                            else
                            {
                                e.Graphics.FillRectangle(new SolidBrush(textCustom.Values[i].Background),
                                    recText + startBuildRect, e.Bounds.Y, GetTextWidth(textCustom.Values[i], e.Graphics) + sizebuild, e.Bounds.Height - 1);
                            }

                            startBuildRect += sizeDrawRect + (int)GetTextWidth(textCustom.Values[i], e.Graphics);
                            sizeDrawRect = 0;
                        }
                    }
                }

                // Draw the texts
                for (int i = 0; i < textCustom.Values.Count; i++)
                {
                    Font fontUse = new Font(cononline.Font.FontFamily, cononline.Font.Size, textCustom.Values[i].Font);

                    if (textCustom.Values[i].EndForeColor != Color.Black)
                    {
                        e.Graphics.DrawString(textCustom.Values[i].Text, fontUse, new LinearGradientBrush(new Point(recText + sizebuild, e.Bounds.Y), 
                            new Point(recText + sizebuild + (int)GetTextWidth(textCustom.Values[i], e.Graphics), e.Bounds.Y),
                            textCustom.Values[i].ForeColor, textCustom.Values[i].EndForeColor), recText + sizebuild, e.Bounds.Y);
                    }
                    else
                        e.Graphics.DrawString(textCustom.Values[i].Text, fontUse,
                            new SolidBrush(textCustom.Values[i].ForeColor), recText + sizebuild, e.Bounds.Y);

                    sizebuild += (int)GetTextWidth(textCustom.Values[i], e.Graphics);
                }

                // Draw the selection rectangle if selected
                if (isSelected)
                    e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(96, Color.Blue)),
                        recText, e.Bounds.Y, sizebuild, e.Bounds.Height - 1);
            }
            else
            {
                if (isSelected)
                    e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(128, Color.Blue)), recText, e.Bounds.Y,
                        TextRenderer.MeasureText(e.Node.Text, cononline.Font).Width, e.Bounds.Height - 1);
                    
                e.Graphics.DrawString(e.Node.Tag != null ? ((Contact)e.Node.Tag).Name : e.Node.Text, cononline.Font, new SolidBrush(cononline.ForeColor),
                    recText, e.Bounds.Y);

                if (e.Node.Nodes.Count != 0)
                {
                    e.Graphics.DrawImage(Image.FromFile(Application.StartupPath + @"\img\other\" + (e.Node.IsExpanded ? "tv_collapse.png" : "tv_expand.png")),
                        recText - 20, e.Bounds.Y + 3, 12, 12);
                }
            }

            if (e.Node.Tag != null && e.Node.Text != (textCustom.Success ? textCustom.Text : ((Contact)e.Node.Tag).Name))
                e.Node.Text = textCustom.Success ? textCustom.Text : ((Contact)e.Node.Tag).Name;

            e.Graphics.Dispose();
        }

        private float GetTextWidth(FontInfo info, Graphics gr)
        {
            bool endsWithSpace = info.Text.EndsWith(" ");
            Font fontUse = new Font(cononline.Font, info.Font);

            // It doesn't measure ending spaces, so we add a vertical line so that the measure will end there.
            return gr.MeasureString(info.Text + (endsWithSpace ? "|" : string.Empty), fontUse).Width - 
                (endsWithSpace ? gr.MeasureString("|", fontUse).Width : 0);
        }

        private void signOutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}
